
(ns model.object
  (:refer-clojure :exclude (merge))
  (:use clojure.contrib.combinatorics)
  )

(require ['model.vector3D :as 'position])
(require ['model.vector3D :as 'vector3D])

(def GRAVCON (* 3.9327 (Math/pow 10 -59)))
(def TimeStep 1)

(defstruct object :pozicija :masa :brzina :ubrzanje :ime)

(defn make
  ([]
    (struct object (position/make) 0 (vector3D/make) (vector3D/make) "masa"))

  ([pozicija masa brzina ubrzanje name]
    (struct object pozicija masa brzina ubrzanje name))
  )

(defn accel_fun [m r]
  (/ (* m GRAVCON) (* r r)))

(defn accel_between [ob1 ob2]
  (let [
    p1 (:pozicija ob1)
    p2 (:pozicija ob2)
    d (position/distance p1 p2)
    v_norm (vector3D/unit (vector3D/sub p2 p1))
    g (accel_fun  (:masa ob2) d)
    ]
    (vector3D/s-mul v_norm g))
  )

(defn sum-accel
  ([obj sis]
    (assoc obj :ubrzanje (sum-accel obj sis (vector3D/make))))

  ([obj sis a]
    (cond
      (empty? sis) a
      (= obj (first sis)) (recur obj (rest sis) a)
      :else (recur obj (rest sis) (vector3D/add a (accel_between obj (first sis))))
      )
    )
  )

(defn accel-all [os]
  (map #(sum-accel % os) os))

(defn position [obj]
  (let [
    a (:ubrzanje obj)
    p (:pozicija obj)
    v (:brzina obj)
    av (vector3D/add v (vector3D/s-mul a TimeStep))
    ]
    (assoc obj :pozicija (position/add p (vector3D/s-mul av TimeStep)))
    )
  )

(defn positioning-all [os]
  (map position os))

(defn collision? [o1 o2]
  (let [
    p1 (:pozicija o1)
    p2 (:pozicija o2)
    ]
    (> 2 (position/distance p1 p2))
    )
  )

(defn center-mass [
  {p1 :pozicija, m1 :masa}
  {p2 :pozicija, m2 :masa}]
  (let [
    m (/ m1 (+ m1 m2))
    uv (vector3D/unit (vector3D/sub p2 p1))
    d (vector3D/s-mul uv m)
    ]
    (position/add p1 d)
    )
  )

(defn merge [
  {n1 :ime, m1 :masa, v1 :brzina f1 :ubrzanje, :as ob1}
  {n2 :ime, m2 :masa, v2 :brzina f2 :ubrzanje, :as ob2}]
  (let [
    p (center-mass ob1 ob2)
    m (+ m1 m2)
    mv1 (vector3D/s-mul v1 m1)
    mv2 (vector3D/s-mul v2 m2)
    v (vector3D/s-mul (vector3D/add mv1 mv2) (/ 1 m))
    f (vector3D/add f1 f2)
    n (if (> m1 m2) (str n1 "." n2) (str n2 "." n1))
    ]
    (make p m v f n)
    )
  )

(defn collision-rm [ob1 ob2 obj]
  (conj
    (->> obj (remove #(= ob1 %)) (remove #(= ob2 %)))
    (merge ob1 ob2))
  )

(defn collision-check [obj]
  (loop [pairs (combinations obj 2) cos obj]
    (if (empty? pairs)
      cos
      (let [
        [ob1 ob2] (first pairs)
        ]
        (if (collision? ob1 ob2)
          (recur (rest pairs) (collision-rm ob1 ob2 cos))
          (recur (rest pairs) cos)
          )
        )
      )
    )
  )

(defn move-all [obj]
  (-> obj collision-check accel-all positioning-all))


